#include <stdio.h>
#include <string.h>
#include "relic.h"
#include "sqft_conf.h"
#include <math.h>
#include <sys/time.h>


int MAX, is_prime;
long long unsigned acc_rabin, acc_sqft;
struct timeval start, stop;
FILE *fp_rabin, *fp_sqft;
bn_t p;


/**--------------------------------------------------------------------*/
/**
 * Funcao para calculo de intervalo de tempo de execuao
 * Precisao: microsegundos (us)
 */
 
long long unsigned getTimeDifff(struct timeval start, struct timeval stop) {
	long long unsigned time;
	time = (float) (stop.tv_sec - start.tv_sec)*1000000;
	time += ((float)(stop.tv_usec - start.tv_usec));
	return time;
}




/**--------------------------------------------------------------------*/

int gen_all_bits() {
	int bits=0;
	int j=0;
	MAX = 20;
	for (bits=8; bits<=BN_BITS; bits *= 2) {
		acc_sqft=0;
		acc_rabin=0;
		printf("Getting times for %d BITS\n", bits);
		
		for (j=0; j<MAX; j++) {
			is_prime=0;
			while (!is_prime) {
				do {
					bn_rand(p, BN_POS, bits);
				} while (bn_bits(p) != bits);
				//bn_gen_prime(p, bits);
				
				gettimeofday(&start, NULL);
				if (bn_is_prime_basic(p)) {
					if (bn_is_prime_rabin(p)) {
						is_prime=1;
					}
				}
				gettimeofday(&stop, NULL);
			
				acc_rabin += getTimeDifff(start, stop);

				gettimeofday(&start, NULL);
				if (bn_is_prime_basic(p)) {
					if (bn_is_prime_sqft(p)) {
						if (!is_prime) {
							printf("\nWTF - ERROR\n");
							return 1;
						}
					}
				}
				gettimeofday(&stop, NULL);
			
				acc_sqft += getTimeDifff(start, stop);
			}
		}
		fprintf(fp_sqft, "%d %llu\n", (int) log2((double) bits), acc_sqft/MAX);
		fprintf(fp_rabin, "%d %llu\n", (int) log2((double) bits), acc_rabin/MAX);
	}
	return 0;
}


/**--------------------------------------------------------------------*/


int gen_lin_bits(int i) {
	int bits=0;
	int j=0;
	
	MAX = 5;
	for (bits=8; bits<=BN_BITS; bits += i) {
		acc_sqft=0;
		acc_rabin=0;
		printf("Getting times for %d BITS\n", bits);
		
		for (j=0; j<MAX; j++) {
			is_prime=0;
			while (!is_prime) {
				do {
					bn_rand(p, BN_POS, bits);
				} while (bn_bits(p) != bits);
				//bn_gen_prime(p, bits);
				
				gettimeofday(&start, NULL);
				if (bn_is_prime_basic(p)) {
					if (bn_is_prime_rabin(p)) {
						is_prime=1;
					}
				}
				gettimeofday(&stop, NULL);
			
				acc_rabin += getTimeDifff(start, stop);

				gettimeofday(&start, NULL);
				if (bn_is_prime_basic(p)) {
					if (bn_is_prime_sqft(p)) {
						if (!is_prime) {
							printf("\nWTF - ERROR\n");
							return 1;
						}
					}
				}
				gettimeofday(&stop, NULL);
			
				acc_sqft += getTimeDifff(start, stop);
			}
		}
		fprintf(fp_sqft, "%f %llu\n", (double) log2((double) bits), acc_sqft/MAX);
		fprintf(fp_rabin, "%f %llu\n", (double) log2((double) bits), acc_rabin/MAX);
	}
	return 0;
}



/**--------------------------------------------------------------------*/


int gen_bits(int bits) {
	int i=0;
	
	for (i=0; i<20; i++) {
		bn_gen_prime(p, bits);
		
		gettimeofday(&start, NULL);
		bn_is_prime_sqft(p);
		gettimeofday(&stop, NULL);
		fprintf(fp_sqft, "%d\t%llu\n", i, getTimeDifff(start, stop));
		
		gettimeofday(&start, NULL);
		bn_is_prime_rabin(p);
		gettimeofday(&stop, NULL);
		fprintf(fp_rabin, "%d\t%llu\n", i, getTimeDifff(start, stop));
	}
	return 0;
}



/**--------------------------------------------------------------------*/

int main(int argc, char *argv[]) {

	int i=0;
	
	if (core_init() != STS_OK) {
		core_clean();
		return 1;
	}
	
	fp_rabin = fopen("sts/rabin.dat", "w");
	fp_sqft = fopen("sts/sqft.dat", "w");
	fprintf(fp_rabin, "# Rabin \n");
	fprintf(fp_sqft, "# SQFT \n");	
	bn_null(p);
	bn_new(p);
	
	if (argc > 1) {
		i = atoi(argv[1]);
		if (i<=8) gen_lin_bits(i);
		else gen_bits(i);
	}
	else gen_all_bits();

	
	
	bn_free(p);
	fclose(fp_rabin);
	fclose(fp_sqft);
	core_clean();
	return 0;
}
